/* 
 * File:   Rationnal.cpp
 * Author: Gab'z
 * 
 * Created on 29 mai 2012, 12:26
 */

#include "Rationnal.h"
#include "Integer.h"
#include "../../../exception/ArithmeticException.h"
#include <sstream> 

namespace calculator {

    Rationnal::Rationnal(int n, int d) :
    SimpleNumber(), _num(n), _denom(d != 0 ? d : 1) {
        if (d == 0) throw ArithmeticException("Rationnal denominator instancied with 0");
        simplifier();
    }

    Rationnal::Rationnal(const Integer& e) :
    SimpleNumber(), _num(e), _denom(1) {
        simplifier();
    }

    Rationnal::Rationnal(const Rationnal& r) :
    SimpleNumber(), _num(r._num), _denom(r._denom) {
        simplifier();
    }

    Rationnal::~Rationnal() {
    }

    Rationnal* Rationnal::clone() const {
        return new Rationnal(_num, _denom);
    }

    std::string Rationnal::toString() const {
        std::ostringstream oss;
        oss << _num;
        if (_denom != 1) oss << "/" << _denom;
        return oss.str();
    }

    double Rationnal::value() const {
        return (double) _num / (double) _denom;
    }

    Rationnal& Rationnal::operator=(const Rationnal& r) {
        if (this == &r) return *this;
        _num = r._num;
        _denom = r._denom;
        return *this;
    }

    Rationnal& Rationnal::operator+=(const Rationnal& r) {
        _num = _num * r._denom + _denom * r._num;
        _denom = _denom * r._denom;
        simplifier();
        return *this;
    }

    Rationnal& Rationnal::operator-=(const Rationnal& r) {
        _num = _num * r._denom - _denom * r._num;
        _denom = _denom * r._denom;
        simplifier();
        return *this;
    }

    Rationnal& Rationnal::operator*=(const Rationnal& r) {
        _num = _num * r._num;
        _denom = _denom * r._denom;
        simplifier();
        return *this;
    }

    Rationnal& Rationnal::operator/=(const Rationnal& r) {
        if (r._num == 0) throw ArithmeticException("Rationnal divided by 0");
        _num = _num * r._denom;
        _denom = _denom * r._num;
        simplifier();
        return *this;
    }

    Rationnal& Rationnal::pow(const Integer& r) {
        if ((int) r.value() == 0) {
            _num = 0;
            _denom = 1;
        } else {
            int num;
            int denom;
            if ((int) r.value() > 0) {
                num = _num;
                denom = _denom;

            } else {
                int tmp = _num;
                num = _denom;
                denom = tmp;

                _num = _denom;
                _denom = tmp;
            }
            for (unsigned int i = 2; i <= ((unsigned int) r.value() > 0 ? (unsigned int) r.value() : -(unsigned int) r.value()); i++) {
                _num *= num;
                _denom *= denom;
            }
        }
        return *this;
    }

    int Rationnal::pgcd(int a, int b) const {
        if (a == 0 || b == 0) return 0;
        if (a < 0) a = -a;
        if (b < 0) b = -b;
        while (a != b) {
            if (a > b) a = a - b;
            else b = b - a;
        }
        return a;
    }

    void Rationnal::simplifier() {
        if (_num == 0) {
            _denom = 1;
            return;
        }
        int p = pgcd(_num, _denom);
        _num /= p;
        _denom /= p;
        if (_denom < 0) {
            _denom = -_denom;
            _num = -_num;
        }
    }

    Rationnal operator+(const Rationnal& r1, const Rationnal& r2) {
        Rationnal r(r1);
        r += r2;
        return r;
    }

    Rationnal operator-(const Rationnal& r1, const Rationnal& r2) {
        Rationnal r(r1);
        r -= r2;
        return r;
    }

    Rationnal operator*(const Rationnal& r1, const Rationnal& r2) {
        Rationnal r(r1);
        r *= r2;
        return r;
    }

    Rationnal operator/(const Rationnal& r1, const Rationnal& r2) {
        Rationnal r(r1);
        r /= r2;
        return r;
    }

    Rationnal pow(const Rationnal& r1, const Integer& e) {
        Rationnal r(r1);
        r.pow(e);
        return r;
    }
}
